////////////////////////////////////////////////////////////////////////////
//
//  Crytek Engine Source File.
//  Copyright (C), Crytek Studios, 2002.
// -------------------------------------------------------------------------
//  File name:   voxman.h
//  Version:     v1.00
//  Created:     28/5/2004 by Vladimir Kajalin
//  Compilers:   Visual Studio.NET
//  Description: 
// -------------------------------------------------------------------------
//  History:
//
////////////////////////////////////////////////////////////////////////////
#ifndef VOX_MAN_H
#define VOX_MAN_H

typedef uint64 ISO_KEY;
const int nBitShift1 = 10 * sizeof(ISO_KEY) / sizeof(uint32);
const int nBitShift2 = nBitShift1*2;

#define TERRAIN_VOX_LAYERS_NUM 8
#define DEF_VOX_VOLUME_SIZE 32 // units
#define DEF_VOX_UNIT_SIZE 2.f // meters
#define VOX_MAX_LODS_NUM 3
#define VOX_MAX_SURF_TYPES_NUM 16
#define VOX_ISO_LEVEL (256*256/2)
#define VOX_MAT_MASK STYPE_BIT_MASK
#define VOX_MAX_USHORT (256*256-1)
const int VOX_HASH_SIZE = 16;
const float VOX_HASH_SCALE = 0.125f;

#if !defined(ARR_TEX_OFFSETS_SIZE_DET_MAT)
#define ARR_TEX_OFFSETS_SIZE_DET_MAT 19
#endif

#pragma pack(push,4)

struct SVoxelChunkVer3
{
	int nChunkVersion;
	Vec3i vOrigin, vSize;
	uint32 nFlags;
	uint16 m_arrVolume[DEF_VOX_VOLUME_SIZE][DEF_VOX_VOLUME_SIZE][DEF_VOX_VOLUME_SIZE];
	char m_arrSurfaceNames[VOX_MAX_SURF_TYPES_NUM][64];
	ColorB m_arrColors[DEF_VOX_VOLUME_SIZE][DEF_VOX_VOLUME_SIZE][DEF_VOX_VOLUME_SIZE];

	AUTO_STRUCT_INFO
};

struct SVoxelChunkVer4
{
  int nChunkVersion;
  Vec3i vOrigin, vSize;
  uint32 nFlags;
  char m_arrSurfaceNames[VOX_MAX_SURF_TYPES_NUM][64];

  AUTO_STRUCT_INFO
};

#pragma pack(pop)

// structures used during compiling
typedef struct 
{
	Vec3 p[3];
	int	arrMatId[3];
	ColorB arrColor[3];
	Vec3 vNormal;
	bool bRemove;
} TRIANGLE;

struct GRIDCELL
{
  GRIDCELL() { ZeroStruct(*this); for(int i=0; i<8; i++) arrColor[i] = Col_Gray; }
	Vec3 p[8];
	float val[8];
	int arrMatId[8];
	ColorB arrColor[8];
};

#include "ObjMan.h"

// this class contain one ambient pass and several light pass RenderMesh's
class CVoxelMesh : public Cry3DEngineBase
{
	SSurfaceTypeInfo m_arrSurfaceTypeInfo[VOX_MAX_LODS_NUM][VOX_MAX_SURF_TYPES_NUM];
	IRenderMesh * m_arrpRM_Ambient[VOX_MAX_LODS_NUM];
	PodArray<uint16> m_arrVertHash[VOX_HASH_SIZE][VOX_HASH_SIZE];
  int m_arrCurrAoRadiusAndScale[VOX_MAX_LODS_NUM];
	static PodArray<SSurfaceType *> m_lstReadyTypes;

public:
	CVoxelMesh(SSurfaceType ** ppSurfacesPalette);
	~CVoxelMesh();

	// render
	void RenderAmbPass(int nLod, int nSort, int nAW, CRenderObject * pObj);
	void RenderLightPasses(int nLod, int nDLMask, class CVoxelObject * pVoxArea, bool bInShadow, float fDistance, const SRendParams & rParams);
	void SetupAmbPassMapping(int nElemCount, float * pHMTexOffsets, const struct SSectorTextureSet & texSet, bool bSetupSinglePassTexIds);

	// create
	void ResetRenderMeshs();
	IRenderMesh * CreateRenderMeshFromIndexedMesh(CMesh * pMaterialMesh, CVoxelObject * pVoxArea, int nLod);
  void MakeRenderMeshsFromMemBlocks(CVoxelObject * pVoxArea);

	// access
	IRenderMesh * GetRenderMesh(int nLod);

	int UpdateAmbientOcclusion(CVoxelObject ** pNeighbours, CVoxelObject * pThisArea, int nLod);
	void UpdateVertexHash(IRenderMesh * pRM);
	int GetAmbientOcclusionForPoint(Vec3 vPos, Vec3 vNorm, float & fResult, CVoxelObject * pThisArea);
  void CheckUpdateLighting(int nLod, CVoxelObject * pSrcArea);
  int GetMemoryUsage();
  void DeleteCContentCGF(CContentCGF * pObj);
};

template <class T> struct Array3d
{
	Array3d() 
	{
		m_pData = NULL;
		m_nSizeX=m_nSizeY=m_nSizeZ=0;
	} 

	~Array3d() 
	{
		delete [] m_pData;
	} 

	T * m_pData;
	int m_nSizeX,m_nSizeY,m_nSizeZ;

	T & GetAt(int x, int y, int z) 
	{ 
		int nPos = x*(m_nSizeY*m_nSizeZ) + y*m_nSizeZ + z;
		assert(nPos>=0 && nPos<GetDataSize());
		return m_pData[nPos]; 
	}

	T & GetAtClamped(int x, int y, int z) 
	{ 
		int nPos = 
			CLAMP(x, 0, m_nSizeX-1)*(m_nSizeY*m_nSizeZ) + 
			CLAMP(y, 0, m_nSizeY-1)*(m_nSizeZ) + 
			CLAMP(z, 0, m_nSizeZ-1); 

		assert(nPos>=0 && nPos<GetDataSize());
		return m_pData[nPos]; 
	}

	void Allocate(int nSizeX, int nSizeY, int nSizeZ) 
	{ 
		if(m_nSizeX != nSizeX || m_nSizeY != nSizeY || m_nSizeZ != nSizeZ )
		{
			delete [] m_pData;
			m_nSizeX = nSizeX;
			m_nSizeY = nSizeY;
			m_nSizeZ = nSizeZ; 
			m_pData = new T [m_nSizeX*m_nSizeY*m_nSizeZ];
		}

		memset(m_pData, 0, m_nSizeX*m_nSizeY*m_nSizeZ*sizeof(T));
	}

	void CopyFrom(Array3d & other) 
	{ 
    Allocate(other.m_nSizeX, other.m_nSizeY, other.m_nSizeZ);
		memcpy(m_pData, other.m_pData, GetDataSize());
	}

	void UpdateFrom(T * otherData, int nDataSize) 
	{ 
		assert(m_pData && nDataSize == GetDataSize());
		memcpy(m_pData, otherData, GetDataSize());
	}

	int GetDataSize() { return m_nSizeX*m_nSizeY*m_nSizeZ*sizeof(T); }
	T * GetElements() { return m_pData; }
};

class CVoxelVolume : public Cry3DEngineBase
{
public:
	static void StaticReset();

public:

	CVoxelVolume(float fUnitSize, int nSizeXinUnits, int nSizeYinUnits, int nSizeZinUnits);

	// array of voxels of the sector
	Array3d<uint16> m_arrVolume;
	Array3d<uint16> m_arrVolumeBackup;
	Array3d<ColorB> m_arrColors;
	float m_fUnitSize;
	CVoxelObject * m_pSrcArea;
	bool m_bUpdateRequested;
	uint32 m_nUpdateRequestedFrameId;

	struct SGenerateTrianglesThreadParams
	{
		CVoxelVolume * pVoxelVolume;
		CVoxelObject ** pNeighbours;
		PodArray<TRIANGLE> lstTris;
		int nLod, X1, X2;
	};

	void SetVolume(uint16 * pData, ColorB * pColors);
	void GetVolume(uint16 * pData, ColorB * pColors);
	void RenderDebug(CVoxelObject ** arrNeighbours);
	bool DoVoxelShape(EVoxelEditOperation eOperation, Vec3 vPos, float fRadius, SSurfaceType * pLayer, Vec3 vBaseColor, EVoxelBrushShape eShape, CVoxelObject ** pNeighbours);
	void SubmitVoxelSpace();
	Vec3i GetVolumeSize() { return Vec3i(m_arrVolume.m_nSizeX,m_arrVolume.m_nSizeY,m_arrVolume.m_nSizeZ); }
	uint16 GetVoxelValue(int x, int y, int z, CVoxelObject ** pNeighbours, int * pMatId, ColorB * pColor);
	CIndexedMesh * Compile(CVoxelObject ** pNeighbours);
	CIndexedMesh * MakeIndexedMesh(PodArray<TRIANGLE> & lstTris, CVoxelObject ** pNeighbours);
	void SimplifyIndexedMesh(CIndexedMesh * pIndexedMesh, int nLod);
	void GenerateTrianglesFromVoxels(CVoxelObject ** pNeighbours, int nLod, PodArray<TRIANGLE> & lstTris, int X1=-1000, int X2=-1000);
	void GenerateTrianglesFromVoxels_MT(CVoxelObject ** pNeighbours, int nLod, PodArray<TRIANGLE> & lstTris);
	void FillCellInfo(GRIDCELL & cell, int x, int y, int z, int v, CVoxelObject ** pNeighbours, const Vec3 & vNodeMin, const Vec3 & vNodeSize);
	static int Polygonise(GRIDCELL grid,float isolevel,TRIANGLE *triangles);
	static Vec3 VertexInterp(float isolevel, Vec3 p1, Vec3 p2, float valp1, float valp2, int nMat1, int nMat2, 
	int*pnMatId, const ColorB & col1, const ColorB & col2, ColorB * colOut);
	float GetVoxelValueInterpolated(float x, float y, float z, CVoxelObject ** pNeighbours, int * pMatId, ColorB * pColor, int nScale);
	int GenerateTrianglesForCell(Vec3i & vCell, CVoxelObject ** pNeighbours, int nLod, PodArray<TRIANGLE> & lstTris, int nStep);
	uint16 GetLocalSurfaceId(SSurfaceType * pLayer);
	void InitVoxelsFromHeightMap(bool bOnlyMaterials);
	uint16 GetHeightMapValue(const Vec3 vPos, uint16 ucDefaultValue, bool bCheckForHole = false);
	uint16 GetLessUsedSurfaceId();
	bool IsEmpty(CVoxelObject ** pNeighbours);
	void SerializeRenderMeshs(CVoxelObject * pVoxArea, CIndexedMesh * pIndexedMesh);
	AABB GetAABB();
	bool SetVolumeData( struct SVoxelChunkVer3 * pChunk, unsigned char ucChildId );
	bool ResetTransformation();
	IMaterial *GetMaterial(Vec3 * pHitPos);
	void InterpolateVoxelData();
	int GetMemoryUsage();
	void CopyHM();
	void ValidateSurfaceTypes();
	void NormalizeVolume(CVoxelObject ** pNeighbours);
	void Upscale(float result[3][3][3], float input[2][2][2], float isolevel);

private:
	static PodArray<Vec3i> s_tmpLstNextCells;
	
};

// class containing voxel data and geometry of terrain sector
class CVoxelObject : public IVoxelObject, public Cry3DEngineBase
{
public:
	static void StaticReset();

public:

	// physical representation
	IPhysicalEntity * m_pPhysEnt;
	phys_geometry * m_pPhysGeom;
	Vec3 m_vPos;
	Matrix34 m_Matrix;
	int m_nEditorObjectId;
	float m_fCurrDistance;
	std::vector<_smart_ptr<CMemoryBlock> > m_arrMeshesForSerialization;
	struct SSurfaceType * m_arrSurfacesPalette[VOX_MAX_SURF_TYPES_NUM];
	int m_arrUsedSTypes[VOX_MAX_SURF_TYPES_NUM];
	CVoxelMesh * m_pVoxelMesh;
	int m_nFlags;
	AABB m_WSBBox;
	CVoxelVolume * m_pVoxelVolume;
	char * m_pObjectName;
	CIndexedMesh * m_pMesh;
	CIndexedMesh * m_pTmpMesh;	// used by CreateIndexedMesh to work around a bad resource lifetime problem
	int m_nSID;

	// 3d terrain related
	void * m_pDataProvider;
	ITexture * m_pNodeTex;
	uint32 m_nScheduleRebuildFrameId;
	uint32 m_nRebuildDoneFrameId;

	CIndexedMesh * GetIndexedMesh() { return m_pMesh; }
	IIndexedMesh * CreateIndexedMesh();	// the mesh returned (m_pTmpMesh) is owned (and destroyed) by this object; check m_pTmpMesh's usage before using this function
	bool InitMaterials();
	void Compile(CVoxelObject ** pNeighbours);
	SSurfaceType * GetGlobalSurfaceType(uint16 ucSurfaceTypeId);
	bool DoVoxelShape(EVoxelEditOperation eOperation, Vec3 vPos, float fRadius, 
	SSurfaceType * pLayer, Vec3 vBaseColor, EVoxelBrushShape eShape, CVoxelObject ** pNeighbours);
	void Physicalize(CMesh * pMesh, std::vector<char> * physData);
	using IRenderNode::Physicalize;
	void DePhysicalize();

	// construct
	CVoxelObject(Vec3 vOrigin, float fUnitSize, int nSizeXinUnits, int nSizeYinUnits, int nSizeZinUnits);
	~CVoxelObject();

	static void RegisterMesh(CVoxelObject* pMesh, bool bRemove = false)
	{
#if defined( PS3 )
		if(bRemove)
		{
			for(TMeshVec::iterator it = sMeshArrAll.begin(), end=sMeshArrAll.end(); it!=end; ++it)
			{
				if(*it == pMesh)
				{
					sMeshArrAll.erase(it);
					break;
				}
			}

			if (sMeshArrAll.empty())
			{
				stl::free_container(sMeshArrAll);
			}
		}
		else sMeshArrAll.push_back(pMesh);
#else
		if(bRemove) sMeshArrAll.Delete(pMesh);
		else sMeshArrAll.Add(pMesh);
#endif
	}

	IRenderMesh * GetRenderMesh(int nLod);
	void SetSurfacesInfo( SVoxelChunkVer3 * pChunk );
	void SetData( struct SVoxelChunkVer3 * pChunk, unsigned char ucChildId );
	void ResetRenderMeshs();
	void GetData(struct SVoxelChunkVer3 & chunk, Vec3 vOrigin);
	void GetData(struct SVoxelChunkVer4 & chunk, Vec3 vOrigin);
	virtual const char * GetEntityClassName(void) const { return "VoxAreaClass"; }
	virtual const char *GetName(void) const { return "VoxAreaName"; }
	virtual Vec3 GetPos(bool) const { return m_vPos; }
	virtual void Render(const SRendParams &RendParams);
	virtual IPhysicalEntity *GetPhysics(void) const { return m_pPhysEnt; }
	virtual void SetPhysics(IPhysicalEntity * pPhys) { assert(!pPhys); }
	virtual void SetMaterial(IMaterial * pMat) { /*assert(!pMat);*/ }
	virtual IMaterial *GetMaterial(Vec3 * pHitPos = NULL);
	virtual IMaterial *GetMaterialOverride() { return NULL; }
	virtual float GetMaxViewDist();
	virtual EERType GetRenderNodeType() { return eERType_VoxelObject; }
	virtual int GetEditorObjectId();
	virtual void SetEditorObjectId(int nEditorObjectId) { m_nEditorObjectId = nEditorObjectId; }
	virtual struct IStatObj * GetEntityStatObj( unsigned int nPartId = 0, unsigned int nSubPartId = 0, Matrix34A* pMatrix = NULL, bool bReturnOnlyVisible = false);
	virtual void GetMemoryUsage(ICrySizer * pSizer) const;
	VIRTUAL void SetFlags(int nFlags);
	VIRTUAL void Regenerate();
	VIRTUAL void CopyHM();
	VIRTUAL void SetMatrix( const Matrix34& mat );
	const Matrix34& GetMatrix( ) { return m_Matrix; }
	VIRTUAL IMemoryBlock * GetCompiledData(EEndian eEndian);
	VIRTUAL void SetCompiledData(void * pData, int nSize, unsigned char ucChildId, EEndian eEndian);
	VIRTUAL void SetObjectName( const char * pName );
	VIRTUAL bool ResetTransformation();
	virtual void ScheduleRebuild();
	VIRTUAL void InterpolateVoxelData();
	bool IsSnappedToTerrainSectors();
	int GetAmbientOcclusionForPoint(Vec3 vPos, Vec3 vNorm, float & fRes);
	void OnRenderNodeBecomeVisible();
	virtual const AABB GetBBox() const { return m_WSBBox; }
	virtual void SetBBox( const AABB& WSBBox ) { m_WSBBox = WSBBox; }
	virtual void OffsetPosition(const Vec3& delta);
	bool SaveIndexedMeshToFile( CMesh * pMesh, const char *sFilename, IChunkFile** pOutChunkFile );
	void SerializeMesh( CMesh * pMesh );
	CContentCGF * LoadFromMemBlock( class CReadOnlyChunkFile * pChunkFile, CMemoryBlock * pMemBlock, CMesh * & pMesh, std::vector<char> * physData );
	void ReleaseMemBlocks();
	int GetCompiledMeshDataSize();
	byte * GetCompiledMeshData(int & nSize, EEndian eEndian);
	void SetCompiledMeshData(byte * pData, int nSize, EEndian eEndian, bool bDataIsAlligned);
	void SavePhysicalizeData( CNodeCGF *pNode );
	void SerializeMeshes(CVoxelObject * pVoxArea, CIndexedMesh * pIndexedMesh);
	IPhysicalEntity * GetPhysics(int id) { return m_pPhysEnt; }
	bool IsEmpty();
	void SetDataProvider(void * pProvider) { m_pDataProvider = pProvider; }
private:
	friend class VoxTerrain;
	friend class CObjManager;

#if defined( PS3 )
	typedef std::vector<CVoxelObject* > TMeshVec;
#else
	typedef PodArray<CVoxelObject*> TMeshVec;
#endif
	static TMeshVec sMeshArrAll;
	static PodArray<char> s_tmpLstMatIndices;
};

#endif
